package com.itheima.spring.beans;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 实现一个默认的bean工厂DefaultBeanFactory来创建bean实例了,DefaultBeanFactory除了需要实现BeanFactory外,
 * 还需要实现Bean定义注册接口BeanDefinitionRegistry,因为要把bean定义注册到bean工厂里面
 */
public class DefaultBeanFactory implements BeanFactory,BeanDefinitionRegistry, Closeable {

    private final Log logger = LogFactory.getLog(getClass());

    /**
     * 用Map来存放bean定义信息
     * key:beanName
     * value:BeanDefinition
     */
    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    /**
     * 用Map来存放创建的bean实例,注意这里只是存放单例bean,多实例每次都要创建新的,不需要存放
     */
    private Map<String,Object> beanMap = new ConcurrentHashMap<>(256);

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegisterException {

        //(1)判断给入的beanName和beanDefinition不能为空
        Objects.requireNonNull(beanName,"beanName is not null.");
        Objects.requireNonNull(beanDefinition,"beanDefinition is not null.");

        //(2)检查给的bean是否合法
        if(!beanDefinition.validate()){
            throw new BeanDefinitionRegisterException("名字为[" + beanName + "] 的bean定义不合法：" + beanDefinition);
        }
        //(3)如果已存在bean定义就抛异常
        if(this.containsBeanDefinition(beanName)){
            throw new BeanDefinitionRegisterException(
                    "名字为[" + beanName + "] 的bean定义已存在:" + this.getBeanDefinition(beanName));
        }

        //(4)把bean定义放到Map里面
        beanDefinitionMap.put(beanName,beanDefinition);

    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        //根据bean的名字从Map里面获取bean定义
        return this.beanDefinitionMap.get(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        //根据bean的名字判断Map里面是否包含bean定义
        return this.beanDefinitionMap.containsKey(beanName);
    }

    /**
     * //根据bean的名字获取bean实例,里面主要做的工作是创建bean实例和对bean实例进行初始化
     * @param name 通过bean name获取bean
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String name) throws Exception {
        return this.doGetBean(name);
    }

    //根据bean的名字获取bean实例,里面主要做的工作是创建bean实例和对bean实例进行初始化
    protected Object doGetBean(String name) throws Exception{
        //Bean name 不能为空
        Objects.requireNonNull(name,"bean name is not null.");
        //先从beanMap里面获取bean实例
        Object instance  = this.beanMap.get(name);

        //如果beanMap里面已存在bean实例就直接返回,不需要走后面的流程了
        if (instance != null){
            return instance;
        }

        //从beanDefintionMap里面获取bean定义信息
        BeanDefinition bd = this.beanDefinitionMap.get(name);
        //bean定义信息不能为空
        Objects.requireNonNull(bd, "beanDefinition不能为空");
        
        //获取bean类型
        Class<?> type = bd.getBeanClass();
        if(type != null){
            //如果bean的类型不为空,并且工厂方法名为空,说明是使用构造方法的方式来创建bean实例
            if(StringUtils.isBlank(bd.getFactoryMethodName())){
                // 构造方法来构造对象
                instance = this.createInstanceByConstructor(bd);
            }else {
                //如果bean的类型不为空,并且工厂方法名不为空,说明是使用静态工厂方法的方式来创建bean实例
                // 静态工厂方法
                instance = this.createInstanceByStaticFactoryMethod(bd);
            }
        }else{
            //如果bean的类型为空,说明是使用工厂bean的方式来创建bean实例
            // 工厂bean方式来构造对象
            instance = this.createInstanceByFactoryBean(bd);
        }

        // 执行初始化方法
        this.doInit(bd, instance);

        //存放单例的bean到beanMap
        if (bd.isSingleton()){
            beanMap.put(name,instance);
        }
        return instance;
    }

    private void doInit(BeanDefinition bd, Object instance) throws Exception {
        // 获取bean定义中的初始化方法,如果存在初始化方法就通过反射去执行初始化方法
        if(StringUtils.isNotBlank(bd.getInitMethodName())){
            Method method = instance.getClass().getMethod(bd.getInitMethodName(), null);
            method.invoke(instance,null);
        }

    }

    // 工厂bean方式来构造对象 反射
    private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {
        //通过bean定义信息中工厂bean的名字获取工厂bean的实例
        Object factoryBean  = this.doGetBean(bd.getFactoryBeanName());
        //通过bean定义信息中工厂方法的名字反射出工厂bean的方法m创建bean实例
        Class<?> beanClass = factoryBean.getClass();
        Method method = factoryBean.getClass().getMethod(bd.getFactoryMethodName(), null);
        return method.invoke(factoryBean,null);
    }

    // 静态工厂方法 反射
    private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {
        //获取bean类型
        Class<?> type = bd.getBeanClass();
        //通过静态工厂方法方法的名字getFactoryMethodName反射出bean的方法m创建bean实例
        Method method = type.getMethod(bd.getFactoryMethodName(), null);
        return method.invoke(type,null);
    }

    //构造方法来构造对象 反射
    private Object createInstanceByConstructor(BeanDefinition bd) throws Exception {
        try {
            //拿到bean的类型,然后调用newInstance通过反射来创建bean实例
            return bd.getBeanClass().newInstance();
        }catch (Exception e){
            logger.error("创建bean的实例异常,beanDefinition：" + bd, e);
            throw e;
        }
    }

    /**
     * 销毁
     * @throws IOException
     */
    @Override
    public void close() throws IOException {
        // 执行单例实例的销毁方法
        for (Map.Entry<String, BeanDefinition> entry : this.beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            try {
                //获取bean定义中的销毁方法,如果存在销毁方法就通过反射去执行销毁方法
                if(beanDefinition.isSingleton() && StringUtils.isNotBlank(beanDefinition.getDestroyMethodName())){
                    //获取bean实例
                    Object instance = this.beanMap.get(beanName);
                    Method method = instance.getClass().getMethod(beanDefinition.getDestroyMethodName(), null);
                    method.invoke(instance,null);
                }
            }catch (Exception e){
                logger.error("执行bean[" + beanName + "] " + beanDefinition + " 的 销毁方法异常！", e);
            }
        }
    }
}
